package com.innovations.web.core.config;

import cn.hutool.json.JSONUtil;
import com.innovations.common.core.page.TableDataInfo;
import com.innovations.common.utils.ExtHelper;
import io.swagger.annotations.ApiOperation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

@Component
@Aspect // 切面（1.将一个java类定义为切面类）
public class AppLogConfig {

    @Autowired
    private Environment env;

    private Logger LOGGER = LoggerFactory.getLogger(AppLogConfig.class);
    private static boolean isDoBefore = false;
    private static boolean isDoAfter = false;
    private static boolean isDoReturning = false;
    private static boolean isDoThrowing = false;

    /**
     * 保证每个线程都有一个单独的实例
     */
    private ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    private static String[] params;

    static {
        params = new String[]{"aspect.logger.spring-application-name", "aspect.logger.request-url", "aspect.logger.request-uri",
                "aspect.logger.request-desc", "aspect.logger.session", "aspect.logger.cookie",
                "aspect.logger.content-type", "aspect.logger.request-method", "aspect.logger.request-ip",
                "aspect.logger.request-user-agent", "aspect.logger.class-method", "aspect.logger.request-param"};
    }

    @PostConstruct
    public void init() {
        isDoBefore = env.getProperty("aspect.do-before") == null ? false : env.getProperty("aspect.do-before", Boolean.class);
        isDoAfter = env.getProperty("aspect.do-after") == null ? false : env.getProperty("aspect.do-after", Boolean.class);
        isDoReturning = env.getProperty("aspect.do-returning") == null ? false : env.getProperty("aspect.do-returning", Boolean.class);
        isDoThrowing = env.getProperty("aspect.do-throwing") == null ? false : env.getProperty("aspect.do-throwing", Boolean.class);
    }

    /**
     * 切点(2.切入点,可以是一个规则表达式，比如下例中某个package下的所有函数，也可以是一个注解等)
     */
    @Pointcut("execution(* com.innovations.inn.controller..*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint) throws Exception {
        if (!isDoBefore) {
            return;
        }
        threadLocal.set(System.currentTimeMillis());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        StringBuilder stringBuilder = new StringBuilder()
                .append("\n=== 请求入参 ==========================================\n");
        // 记录请求的内容
        this.logHandle(joinPoint, params, request, stringBuilder);
        stringBuilder.append("=======================================================");
        LOGGER.info(stringBuilder.toString());

    }

//    @After("pointcut()")
//    public void doAfter(JoinPoint joinPoint) {
//        if (!isDoAfter) {
//            return;
//        }
//        LOGGER.info("\n==== doAfter ==========================================\n" + joinPoint.toString());
//    }

    /**
     * 返回值信息
     *
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "pointcut()")
    public void doAfterReturning(Object ret) {
        if (!isDoReturning) {
            LOGGER.info("\n=== 请求出参 ==========================================\n");
            LOGGER.info("\n==== 请求耗时：" + ((System.currentTimeMillis() - threadLocal.get())) + "ms"
                    + " =====================\n");
            return;
        }
        StringBuilder stringBuilder = new StringBuilder();
        TableDataInfo tableDataInfo = null;
        if (ret instanceof TableDataInfo) {
            tableDataInfo = (TableDataInfo) ret;
        }
//        else if (ret instanceof Map) {
//            Map<String, Object> map = (Map<String, Object>) ret;
//            if (map.containsKey("rows")) {
//                tableDataInfo = (TableDataInfo) map.get("rows");
//            }
//        }
        if (tableDataInfo != null) {
            stringBuilder.append("\n=== 请求出参 ==========================================\n");
            //stringBuilder.append(JSONUtil.toJsonPrettyStr(tableDataInfo));
            stringBuilder.append(JSONUtil.toJsonStr(tableDataInfo));
            stringBuilder.append("\n==== 请求耗时：").append(System.currentTimeMillis() - threadLocal.get())
                    .append("ms").append(" ================================\n");
            LOGGER.info(stringBuilder.toString());
        } else {
            stringBuilder.append("\n=== 请求出参 ==========================================\n");
            stringBuilder.append(ExtHelper.toString(ret));
            stringBuilder.append("\n==== 请求耗时：").append(System.currentTimeMillis() - threadLocal.get())
                    .append("ms").append(" ================================\n");
            LOGGER.info(stringBuilder.toString());
        }
    }

    @AfterThrowing(throwing = "ex", pointcut = "pointcut()")
    public void doThrowing(Throwable ex) {
        if (!isDoThrowing) {
            return;
        }
        LOGGER.error("\n=== doThrowing ==========================================\n" + ex);
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    private String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    Object temp = method.getAnnotation(ApiOperation.class);
                    if (temp != null) {
                        description = method.getAnnotation(ApiOperation.class).value();
                    }
                    break;
                }
            }
        }
        return description;
    }

    /**
     * 处理请求参数输出
     *
     * @param joinPoint
     * @param requestParams
     * @param request
     * @param stringBuilder
     * @throws Exception
     */
    public void logHandle(JoinPoint joinPoint, String[] requestParams, HttpServletRequest request, StringBuilder stringBuilder) throws Exception {
        Map<String, Object> paramMap = new HashMap<>(16);
        String contentType = request.getContentType();
        //paramMap.put("session", request.getSession());
        //paramMap.put("cookie", request.getCookies());
        //paramMap.put("spring-application-name", env.getProperty("spring.application.name"));
        paramMap.put("请求地址：", request.getMethod() + " " + request.getRequestURL());
        //paramMap.put("请求uri：", request.getRequestURI());
        paramMap.put("接口描述：", getControllerMethodDescription(joinPoint));
        //paramMap.put("请求方式：", request.getMethod());
        //paramMap.put("内容类型：", contentType);
        //paramMap.put("类、方法：", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        paramMap.put("请求IP：", request.getRemoteAddr());
        //paramMap.put("代理用户：", request.getHeader("User-Agent"));

        String reqParam = null;
        Object[] o = joinPoint.getArgs();
        if (contentType != null && contentType.contains("multipart/form-data")) {
            MultipartFile file = (MultipartFile) o[0];
            reqParam = file.getOriginalFilename();
        } else {
            if (o != null && o.length > 0) {
                StringBuffer stringBuffer = new StringBuffer();
                switch (request.getMethod()) {
                    case "GET":
                        Map<String, String[]> map = request.getParameterMap();
                        for (Map.Entry<String, String[]> entry : map.entrySet()) {
                            stringBuffer.append(entry.getKey()).append("=").append(cn.hutool.core.util.ArrayUtil.join(entry.getValue(), "|")).append(",");
                        }
                        break;
                    case "POST":
                        if (joinPoint.getArgs().length != 0) {
                            stringBuffer.append(JSONUtil.toJsonStr(joinPoint.getArgs()[joinPoint.getArgs().length - 1]));
                        }
                        break;
                    default:
                        Map<String, String[]> map2 = request.getParameterMap();
                        if (!map2.isEmpty()) {
                            stringBuffer.append("PARAMETER-SDP:");
                            for (Map.Entry<String, String[]> entry : map2.entrySet()) {
                                stringBuffer.append(entry.getKey()).append("=").append(cn.hutool.core.util.ArrayUtil.join(entry.getValue(), "|")).append(",");
                            }
                        }
                        if (joinPoint.getArgs().length != 0) {
                            stringBuffer.append("DATA-SDP:").append(JSONUtil.toJsonStr(joinPoint.getArgs()[joinPoint.getArgs().length - 1]));
                        }
                        break;
                }
                reqParam = stringBuffer.toString();
            }
        }
        paramMap.put("请求入参：", reqParam);
        paramMap.forEach((param, value) -> {
            stringBuilder.append(param + ":" + value + "\n");
        });

        // 按配置输出
//        for (String param : requestParams) {
//            Boolean property = env.getProperty(param, Boolean.class);
//            String p = param.replace("aspect.logger.", "");
//            if (property != null && property && paramMap.containsKey(p)) {
//                stringBuilder.append(p + ":" + paramMap.get(p) + "\n");
//            }
//            stringBuilder.append(p + ":" + paramMap.get(p) + "\n");
//        }

    }

}